<template>
  <div>
    <input type="file" multiple @change="handleFileSelect" />
    <button @click="startUpload">开始上传</button>
    <button @click="pauseUpload">暂停上传</button>
    <button @click="resumeUpload">继续上传</button>
    <div v-for="(fileInfo, index) in selectedFiles" :key="index">
      <p>{{ fileInfo.file.name }}</p>
      <progress :value="fileInfo.progress" max="100"></progress>
      <p>{{ fileInfo.progress }}%</p>
    </div>
  </div>
</template>

<script setup lang="ts">
  import { ref } from 'vue'
  import Flow, { type FlowFile } from '@flowjs/flow.js'
  import { calculateFileMd5 } from '@/hooks/calculateFileMd5'

  // 定义文件信息的类型
  interface FileInfo {
    file: File
    progress: number
    md5: string
    flowFile: FlowFile | null
  }

  // 分片大小
  const CHUNK_SIZE = 5 * 1024 * 1024

  // 存储选中的文件信息
  const selectedFiles = ref<FileInfo[]>([])

  // Flow.js 实例
  const flow = new Flow({
    target: '/api/flowjs/chunk', // 上传地址
    singleFile: false, // 是否单文件上传
    chunkSize: CHUNK_SIZE, // 数据块大小
    forceChunkSize: false, // 强制每个数据块大小一致
    simultaneousUploads: 3, // 同时上传的数量
    query: {}, // 额外参数
    headers: {}, // 额外消息头
    withCredentials: false, // 是否携带cookie。标准 CORS 请求默认不发送或设置任何 cookie
    allowDuplicateUploads: false, // 是否允许重新上传相同的文件
    testChunks: true, // 对服务器上的每个块执行 GET 请求以检查其是否存在
    maxChunkRetries: 3 // 最大重试次数
  })

  // 处理文件选择事件
  const handleFileSelect = async (event: Event) => {
    const input = event.target as HTMLInputElement
    if (input.files) {
      for (let i = 0; i < input.files.length; i++) {
        const file = input.files[i]
        const md5 = await calculateFileMd5(file, CHUNK_SIZE)

        selectedFiles.value.push({
          file,
          progress: 0,
          md5,
          flowFile: null
        })

        flow.opts.generateUniqueIdentifier = () => md5
        flow.addFile(file)
      }
    }
  }

  // 开始上传文件
  const startUpload = () => {
    // 监听全局的文件开始上传事件
    flow.on('fileAdded', (flowFile: Flow.FlowFile) => {
      console.log(`文件 ${flowFile.name} 开始上传`)
    })

    // 监听全局的文件进度事件
    flow.on('fileProgress', (flowFile: Flow.FlowFile) => {
      const targetFileInfo = selectedFiles.value.find(
        (item) => item.md5 === flowFile.uniqueIdentifier
      )

      if (targetFileInfo) {
        const progress = Math.round(flowFile.progress() * 100)
        if (progress > targetFileInfo.progress) {
          targetFileInfo.progress = progress
        }
      }
    })

    // 监听全局的文件上传成功事件
    flow.on('fileSuccess', (flowFile: Flow.FlowFile) => {
      const targetFileInfo = selectedFiles.value.find(
        (item) => item.md5 === flowFile.uniqueIdentifier
      )
      if (targetFileInfo) {
        targetFileInfo.progress = 100
        console.log(`${targetFileInfo.file.name} 上传成功`)
      }
    })

    // 监听全局的文件上传错误事件
    flow.on('fileError', (flowFile: Flow.FlowFile, message: string) => {
      const targetFileInfo = selectedFiles.value.find(
        (item) => item.md5 === flowFile.uniqueIdentifier
      )
      if (targetFileInfo) {
        console.error(`${targetFileInfo.file.name} 上传失败: ${message}`)
      }
    })

    flow.upload()
  }

  // 暂停上传
  const pauseUpload = () => {
    flow.pause()
  }

  // 继续上传
  const resumeUpload = () => {
    flow.resume()
  }
</script>

<style scoped></style>
